home *** CD-ROM | disk | FTP | other *** search
/ Shareware Grab Bag / Shareware Grab Bag.iso / 090 / cmln0286.arc / STATECOM.C < prev    next >
C/C++ Source or Header  |  1986-02-03  |  17KB  |  723 lines

  1. /*
  2.  * statecom.c - state machine preprocessor
  3.  * as described in february 1986 computer language magazine
  4.  *
  5.  * compile options
  6.  * -dNOVOID    = no void type supported by compiler
  7.  * -dBITFIELDS = bitfields are supported by compiler
  8.  * -dAZTEC86 = the perror library routine is used rather than homebrew,
  9.  * should be otherwise self contained
  10.  */
  11.  
  12. char *version = "statecom version 1.0\n";
  13.  
  14. #include <stdio.h>
  15. #include <ctype.h>
  16. #include <debug.h>
  17. /* for those of you without void type */
  18. #ifdef NOVOID
  19. typedef int void;
  20. #endif
  21. /* for those of you without perror() */
  22. #ifndef AZTEC86
  23. void perror(s) 
  24. char *s;
  25. {
  26.     extern int errno;
  27.     fprintf(stderr,"%s : system error %d\n",s,errno);
  28. }
  29. #endif
  30.  
  31. #define FALSE 0
  32. #define TRUE 1
  33. #define fileclose(f) {if (-1==fclose(f)) perror("fclose");}
  34. #define filekill(f)  {if (-1==unlink(f)) perror("unlink");}
  35. #define freemem(m)     {if (-1==free(m)) perror("free");}
  36. #define usizeof(x)   ((unsigned)sizeof(x))
  37. /*
  38.  * external functions
  39.  */
  40. FILE *fopen();
  41. char *savestr();
  42. char *fgets();
  43. char *index();
  44. void *malloc();
  45. char *strcpy(),*strcat();
  46.  
  47. void perror();
  48.  
  49. /*
  50.  * forward definition of process to void
  51.  */
  52. void process();
  53. void copytilblank();
  54. /*
  55.  * structure of state compiler symbol table entries
  56.  */
  57. typedef struct s
  58. {
  59.     struct    s *next;
  60.     char *statename;    /* name of state */
  61. #ifdef BITFIELDS
  62.     unsigned defined:1;    /* defined flag  */
  63.     unsigned referenced:1;    /* referenced */
  64. #else
  65.     char defined;
  66.     char referenced;
  67. #endif
  68. } statedesc, *stateptr;
  69.  
  70. /*
  71.  * global variables
  72.  */
  73. FILE *input,            /* input file */
  74. *output;                /* output file */
  75. statedesc statetable;    /* head of state compiler symbol table */
  76. char currname[64];        /* name of current input file */
  77. char outname[64]         /* name of current output file */
  78. = "stateout.c";            /* defaults to stateout.c */
  79. char *inptr;            /* pointer into current input line */
  80. int inlineno;            /* line number in input file */
  81. char buffer[256];        /* contains current input file line */
  82. char verbose = 0;        /* whether or not to dump table at end */
  83.  
  84. /* state machine compiler keywords */
  85. static char *
  86. keywords[] =            
  87. {
  88.     "machine",        
  89.     "state",
  90.     "endstate",
  91.     "endmachine",
  92.     "nextstate",
  93.     "debug",
  94.     "nodebug",
  95.     "endargs"
  96. };
  97. /* symbolic constants for indexes into keyword array */
  98. #define MACHINE 0
  99. #define STATE   1
  100. #define ENDSTATE 2
  101. #define ENDMACHINE 3
  102. #define NEXTSTATE 4
  103. #define SDEBUG 5
  104. #define SNODEBUG 6
  105. #define ENDDECL 7
  106. /* symbolic constant for size of keyword array */
  107. #define KEYSIZE (usizeof(keywords)/usizeof(char *))
  108.  
  109. /*
  110.  * main function
  111.  */
  112. main(argc,argv)
  113.     char *argv[];
  114. {
  115.     char *dotptr;
  116.     puts(version);
  117.     if (argc == 1)
  118.     {
  119.         strcpy(currname,"statein.s");
  120.         process(stdin);
  121.     }
  122.     else
  123.     {
  124.         while (--argc)
  125.         {
  126.             ++argv;
  127.             if ((*argv)[0] == '-')
  128.             {
  129.                 char *opt = &(*argv)[1];
  130.                 while (*opt)
  131.                 {
  132.                     switch (*opt)
  133.                     {
  134.                     case 'v':
  135.                     case 'V':
  136.                         verbose = 1;
  137.                         break;
  138.                     default:
  139.                         fprintf(stderr,"unrecognized option %c\n",*opt);
  140.                     }
  141.                     opt++;
  142.                 }
  143.                 continue; /* look for another argument */
  144.             }
  145.             strcpy(currname,*argv);
  146.             strcpy(outname,currname);    /* copy to output name */
  147.             /* if no extension is specified assume .s
  148.              */
  149.             if (NULL == (dotptr = index(currname,'.')))
  150.             {
  151.                 strcat(currname,".s");
  152.             }
  153.             if (NULL == (input = fopen(currname, "r")))
  154.             {
  155.                 perror("statecom");
  156.                 continue;
  157.             }
  158.             /* blithely overwrite corresponding
  159.                C source file
  160.              */
  161.             if (NULL == (dotptr = index(outname,'.')))
  162.             {
  163.                 strcat(outname,".c");
  164.             }
  165.             else
  166.             {
  167.                 ++dotptr;
  168.                 *dotptr++ = 'c';
  169.                 *dotptr = '\0'; /* probably unnecessary */
  170.             }
  171.  
  172.             /* initialize state table variable */
  173.             statetable.next = NULL;
  174.             statetable.statename = "";
  175.             statetable.referenced = FALSE;
  176.             statetable.defined = FALSE;
  177.             process(input);
  178.             reset_parser();
  179.         }
  180.     }
  181. }
  182.  
  183. /*
  184.  * newstate - enters a new state name into symbol table.
  185.  * returns a pointer to the new symbol
  186.  */
  187.  
  188. stateptr
  189. newstate(name)
  190. char *name;
  191. {
  192.     register stateptr current;
  193.     if (NULL == (current = (stateptr)malloc(usizeof(statedesc))))
  194.     {
  195.         perror("statecom");
  196.         exit(1);
  197.     }
  198.     current->statename = savestr(name);
  199.     current->next = statetable.next;
  200.     statetable.next = current;
  201.     current->defined = FALSE;
  202.     current->referenced = FALSE;
  203.     return current;
  204. }
  205.  
  206. /* 
  207.  * reset_parser
  208.  * clears out all symbol table allocations to start on new file 
  209.  */
  210.  
  211. reset_parser()
  212. {
  213.     register stateptr kill = statetable.next;
  214.     register stateptr tmp;
  215.     while (kill)
  216.     {
  217.         tmp = kill;
  218.         kill = kill->next;
  219.         /*lint -e516 */
  220.         freemem(tmp->statename);
  221.         freemem(tmp);
  222.         /*lint +e516 */
  223.     }
  224.     statetable.next = NULL;
  225. }
  226.  
  227. /*
  228.  * print out the symbol table
  229.  */
  230.  
  231. void dump_table()
  232. {
  233.     void print_state();
  234.     register stateptr next = statetable.next;
  235.     while(next)
  236.     {
  237.         print_state(next);
  238.         next = next->next;
  239.     }
  240. }
  241.  
  242. /*
  243.  * print out information about a state
  244.  */
  245.  
  246. void print_state(state)
  247.     register stateptr state;
  248. {
  249.     fprintf(stderr,"%s %s %s\n",state->statename,
  250.         state->defined ? "defined" : "undefined",
  251.         state->referenced ? "referenced" : "unreferenced" );
  252. }
  253.  
  254. /*
  255.  * instatetable
  256.  * returns pointer to symbol table entry matching name,
  257.  * or NULL if none is found
  258.  */
  259.  
  260. stateptr
  261. instatetable(name)
  262. char *name;
  263. {
  264.     register stateptr current = statetable.next;
  265.     while(current)
  266.     {
  267.         if (0 == strncmp(current->statename,name,strlen(name)))
  268.             return current;
  269.         current = current->next;
  270.     }
  271.     return NULL;
  272. }
  273.  
  274. /*
  275.  * cleanup
  276.  * deletes all open files for a fatal error abort
  277.  */
  278.  
  279. void
  280. cleanup()    /* get rid of temporary files */
  281. {
  282.     fileclose(output); 
  283.     filekill(outname);
  284.     exit(1);
  285. }
  286.  
  287. /*
  288.  * serror
  289.  * prints out msg, plus the current input line
  290.  */
  291.  
  292. serror(msg,fatal)
  293.     char *msg;
  294. {
  295.     fprintf(stderr,"line %d : %s\n%s\n", inlineno,buffer,msg);
  296.     if (fatal)
  297.     {
  298.         dump_table();
  299.         cleanup();
  300.     }
  301. }
  302.  
  303. /*
  304.  * process
  305.  * steps through an input file, and generates a c source file
  306.  */
  307.  
  308. void
  309. process(infile)
  310.     FILE *infile;
  311. {
  312.     register int i;            /* good old loop counter i */
  313.     char machinename[64];
  314.     char margs[64];
  315.     char statename[64];
  316.     char targetname[64];
  317.     char *skipblanks(),*skipnonblanks(),*nexttoken();
  318.     stateptr currstate;
  319.     int inmachine = FALSE, instate = FALSE;
  320.     int indeclaration = FALSE;
  321.  
  322.     if (NULL == (output = fopen(outname,"w")))
  323.     {
  324.         perror("statecom");
  325.         exit(1);
  326.     }
  327.  
  328.     inlineno = 0;    /* line incremented after reading each line */
  329.  
  330.     /* put out a #line directive at top of file */
  331.     fprintf(output,"#line 1 \"%s\"\n",currname);
  332.     /* make pass through the source file */
  333.     while (NULL != fgets(buffer,sizeof(buffer),infile))
  334.     {
  335.         inlineno++;
  336.         if (NULL == (inptr = skipblanks(buffer)) || (*inptr != '$') )
  337.         /* if just white space or if first non blank character isn't $ */
  338.         {
  339.             /* pass thru unmodified */
  340.             fputs(buffer,output);
  341.             DEBUGGER(fprintf(stderr,"%sno $ seen\n",buffer););
  342.             continue;
  343.         }
  344.         DEBUGGER(fprintf(stderr,"%s$ seen\n",buffer););
  345.         /* otherwise we have a command to process */
  346.         ++inptr;    /* point past $ */
  347.         for (i = 0; i <= KEYSIZE ; i++)
  348.         {
  349.             if (0 == strncmp(keywords[i],inptr,strlen(keywords[i])))
  350.             {
  351.                 switch(i)
  352.                 {
  353.                 case MACHINE:
  354.                     if (inmachine)
  355.                     {
  356.                         /* assume we just forgot an end of state */
  357.                         serror("nested state machines",FALSE);
  358.                         emitmend(machinename);
  359.                     }
  360.                     inmachine = TRUE;    /* in a state machine */
  361.                     indeclaration = TRUE;    /* waiting for first state */
  362.                     /* skip forward to first parm */
  363.                     inptr = nexttoken(inptr);
  364.                     /* copy name to machine name */
  365.                     copytilblank(machinename,inptr);
  366.                     if (instatetable(machinename))
  367.                     /* may or may not work, depends on compiler, assume fatal */
  368.                         serror("machine name conflicts with state name",TRUE);
  369.                     /* advance inptr to next string */
  370.                     inptr = nexttoken(inptr);
  371.  
  372.                     /* copy name to state name */
  373.                     copytilblank(statename,inptr);
  374.  
  375.                     /* advance inptr to arg list */
  376.                     inptr = nexttoken(inptr);
  377.                     /* copy rest of line to argument clause */
  378.                     strcpy(margs,inptr);
  379.  
  380.                     /* add state to table if necessary */
  381.                     if (NULL == instatetable(statename))
  382.                     {
  383.                         currstate = newstate(statename);
  384.                         currstate->referenced = TRUE;
  385.                         currstate->defined = FALSE;
  386.                     }
  387.  
  388.                     emitmachine(machinename,margs);
  389.                     break;
  390.                 case ENDDECL:
  391.                     if (instate)
  392.                     {
  393.                         serror("Bad place for $enddecl",FALSE);
  394.                         break;    /* fall out of switch without making a mess */
  395.                     }
  396.                     if (!inmachine)
  397.                     /* would cause bad code */
  398.                         serror("Not in machine",TRUE);
  399.                     fputs("{\n",output);
  400.                     fprintf(output,"#line %d \"%s\"\n",inlineno+1,currname);
  401.                     break;
  402.                 case STATE:
  403.                     if (instate)
  404.                     {
  405.                         /* assume end of state was just left out */
  406.                         serror("Nested states",FALSE);
  407.                         emitendstate(statename);
  408.                     }
  409.                     if (!inmachine)
  410.                     /* would generate bad code */
  411.                         serror("Not in machine",TRUE);
  412.                     instate = TRUE;
  413.                     if (indeclaration)
  414.                     {
  415.                         indeclaration = FALSE;
  416.                         emitdeclend(machinename,statename);
  417.                     }
  418.                     inptr = nexttoken(inptr);
  419.                     /* copy token to state name */
  420.                     copytilblank(statename,inptr);
  421.  
  422.                     /* check to see if its defined */
  423.                     if (0 != (currstate = instatetable(statename)))
  424.                     {
  425.                         if (currstate->defined)
  426.                         /* multiply defined goto labels wouldn't work */
  427.                             serror("Multiply defined state",TRUE);
  428.                         else
  429.                             ;    /* referenced but not defined */
  430.                     }
  431.                     else
  432.                     {
  433.                         /* add to table */
  434.                         currstate = newstate(statename);
  435.                     }
  436.                     /* we're defining it here */
  437.                     currstate->defined = TRUE;
  438.                     emitstate(statename);
  439.                     break;
  440.                 case ENDSTATE:
  441.                     if (!instate)
  442.                     {
  443.                         /* just ignore this, for argument's sake */
  444.                         serror("In no state",FALSE);
  445.                         break;
  446.                     }
  447.                     if (!inmachine)
  448.                     {
  449.                         /* just ignore */
  450.                         serror("Not in machine",FALSE);
  451.                         break;
  452.                     }
  453.                     instate = FALSE;
  454.                     inptr = nexttoken(inptr);
  455.                     copytilblank(targetname,inptr);
  456.                     if (0 != 
  457.                         strncmp(statename,targetname,strlen(statename)))
  458.                         /* picky, picky picky! */
  459.                         serror("$endstate doesnt match $state",FALSE);
  460.                     emitendstate(targetname);
  461.                     break;
  462.                 case ENDMACHINE:
  463.                     if (instate)
  464.                     {
  465.                         serror("Unterminated state",FALSE);
  466.                         emitendstate(statename);
  467.                     }
  468.                     if (!inmachine)
  469.                         serror("Not in machine",TRUE);
  470.                     inmachine = FALSE;
  471.                     inptr = nexttoken(inptr);
  472.                     copytilblank(targetname,inptr);
  473.                     if (0 != 
  474.                         strncmp(machinename,targetname,strlen(machinename)))
  475.                         serror("$endmachine doesn't match $machine",FALSE);
  476.                     emitmend(machinename);
  477.                     break;
  478.                 case NEXTSTATE:
  479.                     if (!instate)
  480.                     {
  481.                         /* just ignore */
  482.                         serror("Not in state",FALSE);
  483.                         break;
  484.                     }
  485.                     if (!inmachine)
  486.                     {
  487.                         /* just ignore */
  488.                         serror("Not in machine",TRUE);
  489.                         break;
  490.                     }
  491.                     inptr = nexttoken(inptr);
  492.                     /* copy token to state name */
  493.                     copytilblank(targetname,inptr);
  494.                     /* terminal is a 'meta-state', and doesn't ever get
  495.                        put into state table
  496.                      */
  497.                     if (0 != strcmp("terminal",targetname))
  498.                     {
  499.                         /* check to see if its defined */
  500.                         if (NULL == (currstate = instatetable(targetname)))
  501.                         {
  502.                             /* add to table */
  503.                             currstate = newstate(targetname);
  504.                         }
  505.                         /* set it referenced */
  506.                         currstate->referenced = TRUE;
  507.                     }
  508.                     fprintf(output,"/* $ */ goto %s;\n",targetname);    
  509.                     break;
  510.                 case SDEBUG:
  511.                     fputs(
  512.                     "\n#ifdef SNODEBUG\n#undef SNODEBUG\n#endif\n",output);
  513.                     fprintf(output,"#line %d \"%s\"\n",inlineno,currname);
  514.                     break;
  515.                 case SNODEBUG:
  516.                     fputs("\n#ifndef SNODEBUG\n#define SNODEBUG\n#endif\n",
  517.                     output);
  518.                     fprintf(output,"#line %d \"%s\"\n",inlineno,currname);
  519.                     break;
  520.                 };
  521.                 break;    /* once we find one keyword don't look for more */
  522.             }
  523.         }
  524.         /* if we didn't find key words, a stray $ at beginning of line */
  525.         if (i == KEYSIZE+1)
  526.             serror("No keyword recognized\n",FALSE);
  527.     }
  528.     /* check for stupid errors */
  529.     if (instate)
  530.     {
  531.         fprintf(stderr,"End of file\nunterminated state %s\n" ,statename);
  532.         cleanup();
  533.     }
  534.     if (inmachine)
  535.     {
  536.         fprintf(stderr,"End of file\nunterminated machine %s\n",machinename);
  537.         cleanup();
  538.     }
  539.  
  540.     currstate = statetable.next;
  541.     while (currstate)
  542.     {
  543.         if (currstate->referenced && !currstate->defined)
  544.         {
  545.             fprintf(stderr,"ERROR: state %s referenced but not defined\n",
  546.                 currstate->statename);
  547.             cleanup();
  548.         }
  549.         if (currstate->defined && !currstate->referenced)
  550.         {
  551.             fprintf(stderr,"WARNING: state %s defined but not referenced\n",
  552.                 currstate->statename);
  553.         }
  554.         currstate = currstate->next;
  555.     }
  556.  
  557.     if (verbose)
  558.         dump_table();
  559.  
  560.     fprintf(stderr,"Done\n");
  561.     fileclose(output); 
  562. }
  563.  
  564. /*
  565.  * emitmachine
  566.  * outputs the main state machine function, with argument list
  567.  */
  568.  
  569. emitmachine(machinename,argclause)
  570.     char *machinename,*argclause;
  571. {
  572.     fprintf(output,"\n/*\n * statemachine %s\n */\n",machinename);
  573.     fprintf(output,"%s %s\n",machinename,argclause);    /* first line */
  574.     fprintf(output,"#line %d \"%s\"\n",inlineno+1,currname);
  575. }
  576.  
  577. emitmend(machinename)
  578.     char *machinename;
  579. {
  580.     fputs("\n/*\n * BAD STATE LABEL\n */\nbadstate:\n",output);
  581.     fputs("\n\tfprintf(stderr,",output);
  582.     fputs("\"Fallen off end of a state!!!\\n\");\n"
  583.     ,output);
  584.     fputs("\n\treturn -1;\n",output);
  585.     fputs("\n/*\n * TERMINAL STATE LABEL\n */\nterminal:\n",
  586.     output);
  587.     fputs("\n\treturn 0;\n",output);
  588.     fprintf(output,
  589.         "\n/*\n * end of state machine %s\n */\n}\n"
  590.         ,machinename);
  591. }
  592.  
  593. /*
  594.  * emitstate
  595.  * emits a function declaration and opening brace
  596.  */
  597.  
  598. emitstate(statename)
  599.     char *statename;
  600. {
  601.     fprintf(output,"\n%s:\n",statename);    /* first line */
  602.     fputs("{\n",output);
  603.     fprintf(output,"#line %d \"%s\"\n",inlineno+1,currname);
  604. }
  605.  
  606. /*
  607.  * emitendstate
  608.  * emits a function termination clause
  609.  */
  610.  
  611. emitendstate(statename)
  612.     char *statename;
  613. {
  614.     /* last line (call to badstate) */
  615.     fputs("\n#ifndef SNODEBUG\n",output);
  616.     fputs("goto badstate;\n",output);    
  617.     fputs("#endif\n",output);
  618.     fprintf(output,"/* \n * $endstate %s\n */\n};\n",statename);
  619.     fprintf(output,"#line %d \"%s\"\n",inlineno+1,currname);
  620. }
  621.  
  622. /*
  623.  * emit the end of machine local variables declarations
  624.  */
  625.  
  626. emitdeclend(machinename,statename)
  627.     char *machinename, *statename;
  628. {
  629.     fprintf(output,"\n/*\n * end of declarations for %s\n */\n",machinename);
  630.     fprintf(output,"/* $ */ goto %s;\n",statename);
  631.     fprintf(output,"#line %d \"%s\"\n",inlineno+1,currname);
  632. }
  633.  
  634. /*
  635.  * savestr
  636.  * make a copy of a string to dynamically allocated memory
  637.  * and return a pointer to it
  638.  */
  639.  
  640. char *
  641. savestr(s)
  642.     register char *s;
  643. {
  644.     register char *r;
  645.     /* squirrel away matched file name */
  646.     if (NULL == (r = malloc((unsigned)(strlen(s)+1))))
  647.         serror("Out of memory",TRUE);
  648.     strcpy(r,s);
  649.     r[strlen(s)] = '\0';
  650.     return r;
  651. }
  652.  
  653. /* nexttoken
  654.  * inptr points at start of a token string
  655.  * nexttoken advances it to the start of the next token string 
  656.  */
  657.  
  658. char *
  659. nexttoken(string)    
  660.     register char *string;
  661. {
  662.     char *skipblanks(); char *skipnonblanks();
  663.     /* point past keyword */
  664.     if (NULL == (string = skipnonblanks(string)))
  665.     errorout:
  666.         serror("missing keyword argument",TRUE);
  667.     if (NULL == (string = skipblanks(string)))
  668.         goto errorout;
  669.     return string;
  670. }
  671.  
  672. /*
  673.  * skipblanks
  674.  * returns pointer to first non-blank character in string
  675.  * or null if end of string is found
  676.  */
  677.  
  678. char *
  679. skipblanks(string)
  680.     register char *string;
  681. {
  682.     while (isspace(*string))
  683.         ++string;
  684.     if (*string == '\0')
  685.         return NULL;
  686.     return string;
  687. }
  688.  
  689. /*
  690.  * skipnonblanks
  691.  * returns pointer to first blank character in string
  692.  * or null if end of string is found
  693.  */
  694.  
  695. char *
  696. skipnonblanks(string)
  697.     register char *string;
  698. {
  699.     while ( !isspace(*string) )
  700.         ++string;
  701.     if (*string == '\0')
  702.         return NULL;
  703.     return string;
  704. }
  705.  
  706. /*
  707.  * copytilblank
  708.  * copy from source to target until a blank is found
  709.  */
  710. void copytilblank(target,source)
  711.     char target[];
  712.     register char *source;
  713. {
  714.     register int i;
  715.     /* copy name to target name */
  716.     for(i = 0; *source && !isspace(*source); i++)
  717.         target[i] = *source++;
  718.     target[i] = '\0';    /* terminate name */
  719.     DEBUGGER(fprintf(stderr,"token = %s\n",target);)
  720. }
  721. ted machine %s\n",machinename);
  722.         cleanup();
  723.     }